/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "std_smbus.h"

namespace dev {

std_smbus::std_smbus(mc::engine::abstract_object* parent) : mc::core::object(parent) {
}

std_smbus::~std_smbus() {
}

std::string std_smbus::send_request(mc::mutable_dict request) {
    return std_smbus::_send_request_internal(request);
}
void std_smbus::append_data(mc::mutable_dict& track_request, const mc::mutable_dict& request) {
    if (!request.contains("align_len")) {
        return;
    }

    uint32_t    align_len = request["align_len"].as<uint32_t>();
    std::string data      = track_request["data"].as<std::string>();
    uint32_t    len       = data.length() % align_len;

    if (len != 0) {
        std::string padding(align_len - len, '\x00');
        track_request["data"] = data + padding;
    }
}
std::string std_smbus::_send_request_internal(mc::mutable_dict& request) {
    // 构建跟踪请求结构
    mc::mutable_dict track_request;
    track_request["opcode"] = request["opcode"].as<uint16_t>();

    track_request["data_written"] =
        request.contains("offset") && request["offset"].as<int32_t>() != -1
            ? request["offset"].as<uint32_t>()
            : mc::variant(static_cast<uint32_t>(0));
    track_request["offset_invalid"] =
        request.contains("offset") && request["offset"].as<int32_t>() == -1;

    track_request["len"]           = mc::variant(static_cast<uint32_t>(0));
    track_request["max_frame_len"] = request.contains("max_frame_len")
                                         ? request["max_frame_len"].as<uint32_t>()
                                         : get_max_frame_len();
    track_request["data"] = request.contains("data") ? request["data"].as<std::string>() : "";
    track_request["arg"]  = request.contains("arg") ? request["arg"].as<uint32_t>()
                                                    : mc::variant(static_cast<uint32_t>(0));
    track_request["expect_data_len"]   = request.contains("expect_data_len")
                                             ? request["expect_data_len"].as<int32_t>()
                                             : mc::variant(static_cast<int32_t>(-1));
    track_request["data_object_index"] = request.contains("data_object_index")
                                             ? request["data_object_index"].as<uint32_t>()
                                             : mc::variant(static_cast<uint32_t>(0));

    // 按字节对齐要求补齐data
    append_data(track_request, request);

    // 1、请求包含expect_data_len 并且expect_data_len不为0
    if (request.contains("expect_data_len") && request["expect_data_len"].as<int32_t>() != 0) {
        // 读请求，不定长度，按最大长度读
        if (request["expect_data_len"].as<int32_t>() < 0) {
            track_request["len"] = track_request["max_frame_len"].as<uint32_t>();
        } else {
            // 可能是1、 读请求，固定长度； 2、写请求，回读状态。取最大长度和expect_data_len的最小值
            track_request["len"] =
                std::min(request["expect_data_len"].as<int32_t>(),
                         static_cast<int32_t>(track_request["max_frame_len"].as<uint32_t>()));
        }
    }
    // 2、请求不包含expect_data_len，或者expect_data_len为0并且有data
    else if (request.contains("expect_data_len") &&
             track_request["data"].as<std::string>().length() > 0) {
        track_request["len"] =
            std::min(track_request["max_frame_len"].as<uint32_t>(),
                     static_cast<uint32_t>(track_request["data"].as<std::string>().length()));
    }
    // 3、请求不包含expect_data_len 或者expect_data_len为0 并且data，默认分支
    else {
        track_request["len"] = track_request["max_frame_len"].as<uint32_t>();
    }

    if (request.contains("batch_write") && request["batch_write"].as<bool>()) {
        return batch_write(track_request);
    } else if (request.contains("write_without_read") && request["write_without_read"].as<bool>()) {
        return write_without_read(track_request);
    } else {
        return write_and_read(track_request);
    }
}

std::string std_smbus::write_and_read(mc::mutable_dict& track_request) {
    const uint32_t   max_retry_count    = 3;
    uint32_t         retry_count        = 0;
    mc::mutable_dict final_response     = get_response_empty_obj();
    bool             is_read_completed  = false;
    bool             is_write_completed = false;

    do {
        auto [ok, rsp_raw] = write_read(track_request);
        if (ok) {
            // 重置重试counter
            retry_count = 0;
            track_request["data_written"] =
                track_request["data_written"].as<uint32_t>() + track_request["len"].as<uint32_t>();
            is_write_completed = track_request["data_written"].as<uint32_t>() >=
                                 track_request["data"].as<std::string>().length();
            is_read_completed = _concat_response(final_response, rsp_raw);
        } else {
            if (retry_count < m_max_retry_count) {
                retry_count++;
                dlog(
                    "failed to retrieve response protocol: ${name}, msg:${msg}. Retry: "
                    "${retry}/${max}",
                    ("name", m_name)("msg", rsp_raw)("retry", retry_count)("max", max_retry_count));
            } else {
                elog("failed to retrieve response protocol: ${name}, msg:${msg}. Reach max retry, "
                     "exit",
                     ("name", m_name)("msg", rsp_raw));
                return "";
            }
        }
    } while (!(is_write_completed && is_read_completed));

    return final_response["data"].as<std::string>();
}

bool std_smbus::_concat_response(mc::mutable_dict&       final_response,
                                 const mc::mutable_dict& rsp_raw) {
    // 第一帧 保存数据头部部分
    if (final_response["data"].as<std::string>().empty()) {
        final_response["error_code"]   = rsp_raw["error_code"];
        final_response["opcode"]       = rsp_raw["opcode"];
        final_response["total_length"] = rsp_raw["total_length"];
        final_response["length"]       = 0;
    }

    // 最后一帧 ，仅保留有效数据部分
    if (final_response["length"].as<uint32_t>() + rsp_raw["length"].as<uint32_t>() >=
        final_response["total_length"].as<uint32_t>()) {
        uint32_t start_offset = 1;
        uint32_t end_offset =
            rsp_raw["total_length"].as<uint32_t>() - final_response["length"].as<uint32_t>();
        std::string current_data = final_response["data"].as<std::string>();
        std::string rsp_data     = rsp_raw["data"].as<std::string>();
        final_response["data"]   = current_data + rsp_data.substr(start_offset - 1, end_offset);

        final_response["length"] = final_response["total_length"];
        return true;
    }

    std::string current_data = final_response["data"].as<std::string>();
    std::string rsp_data     = rsp_raw["data"].as<std::string>();
    final_response["data"]   = current_data + rsp_data;

    final_response["length"] =
        final_response["length"].as<uint32_t>() + rsp_raw["length"].as<uint32_t>();
    return false;
}

std::string std_smbus::batch_write(mc::mutable_dict& track_request) {
    // TODO  待Chip batch_write 实现后再实现
    elog("batch_write not implemented");
    return "";
}
std::string std_smbus::write_without_read(mc::mutable_dict& track_request) {
    // std_smbus无此场景
    elog("write_without_read not implemented");
    return "";
}

void std_smbus::init(mc::mutable_dict& params) {
    m_buffer_len = params["BufferLen"].as<uint32_t>();
    m_mcu_address =
        params.contains("McuAddress") ? params["McuAddress"].as<uint32_t>() : DEFAULT_MCU_ADDRESS;
}

mc::mutable_dict std_smbus::get_response_empty_obj() {
    mc::mutable_dict response =
        mc::mutable_dict{{"error_code", static_cast<uint8_t>(smbus_error::SUCCESS)},
                         {"opcode", 0},
                         {"length", 0},
                         {"total_length", 0},
                         {"data", ""}};
    return response;
}

uint32_t std_smbus::get_max_frame_len() {
    assert(m_buffer_len >= header_len && "m_buffer_len must be >= header_len");
    return m_buffer_len - header_len;
}

std::pair<bool, mc::mutable_dict> std_smbus::write_read(mc::mutable_dict& track_request) {
    // 避免read 和 write 之间没有等待时间
    std::this_thread::sleep_for(std::chrono::milliseconds(write_read_delay));

    auto [ok, msg] = send_and_receive_request_in_frames(track_request);
    if (!ok) {
        return {ok, mc::mutable_dict{}};
    }

    // 接收
    return _unpack_request(msg, track_request);
}

std::pair<bool, std::vector<uint8_t>>
std_smbus::send_and_receive_request_in_frames(mc::mutable_dict& track_request) {
    constexpr uint8_t header_len = 2;

    // 最后一帧只发剩余长度， 其他帧发最大长度
    calc_last_frame_len(track_request);

    auto req_bin = construct_request_data(track_request);
    if (req_bin.empty()) {
        return {false, std::vector<uint8_t>()};
    }

    // 加2是因为要包括最首的一个数据总长，最尾的一个PEC校验
    return send_and_receive(req_bin, track_request["len"].as<uint32_t>() + header_len + 2);
}

void std_smbus::calc_last_frame_len(mc::mutable_dict& track_request) {
    auto data_written = track_request["data_written"].as<uint32_t>();
    auto data         = track_request["data"].as<std::string>();

    if (!data.empty()) {
        auto max_frame_len = track_request["max_frame_len"].as<uint32_t>();
        if ((data_written + max_frame_len) > data.length()) {
            track_request["len"] = data.length() - data_written;
        }
    }
}

std::string std_smbus::construct_request_data(mc::mutable_dict& track_request) {
    auto data         = track_request["data"].as<std::string>();
    auto data_written = track_request["data_written"].as<uint32_t>();
    auto len          = track_request["len"].as<uint32_t>();

    std::string write_data;
    // 处理边界问题
    if (data_written + len > data.length()) {
        write_data = "";
    } else {
        write_data = data.substr(data_written, len);
    }

    auto is_write_completed = (data_written + len) >= data.length();

    mc::mutable_dict request{{"count", header_len + write_data.length()}};
    mc::mutable_dict body{{"lun", 0}};

    if (is_write_completed) {
        body["lun"] = 0x80;
    }

    body["args"] = track_request.contains("arg") ? track_request["arg"].as<uint8_t>() : 0;
    body["lun"] =
        body["lun"].as<uint8_t>() | (track_request.contains("data_object_index")
                                         ? track_request["data_object_index"].as<uint8_t>()
                                         : 0);
    body["opcode"] = track_request["opcode"];
    body["offset"] =
        track_request.contains("offset_invalid") && track_request["offset_invalid"].as<bool>()
            ? 0
            : data_written;
    body["length"] = len;

    request["body"] = body;

    return pack_smbus_request(request) + write_data;
}

std::string std_smbus::pack_smbus_request(const mc::mutable_dict& request) {
    std_smbus_request_t smbus_req;
    smbus_req.count  = request["count"].as<uint8_t>();
    smbus_req.lun    = request["body"]["lun"].as<uint8_t>();
    smbus_req.arg    = request["body"]["args"].as<uint8_t>();
    smbus_req.opcode = request["body"]["opcode"].as<uint16_t>();
    smbus_req.offset = request["body"]["offset"].as<uint32_t>();
    smbus_req.length = request["body"]["length"].as<uint32_t>();

    std::string result;
    result.append(reinterpret_cast<const char*>(&smbus_req.count), sizeof(smbus_req.count));
    result.append(reinterpret_cast<const char*>(&smbus_req.lun), sizeof(smbus_req.lun));
    result.append(reinterpret_cast<const char*>(&smbus_req.arg), sizeof(smbus_req.arg));
    result.append(reinterpret_cast<const char*>(&smbus_req.opcode), sizeof(smbus_req.opcode));
    result.append(reinterpret_cast<const char*>(&smbus_req.offset), sizeof(smbus_req.offset));
    result.append(reinterpret_cast<const char*>(&smbus_req.length), sizeof(smbus_req.length));
    return result;
}

std::pair<bool, mc::mutable_dict>
std_smbus::_unpack_request(const std::vector<uint8_t>& data,
                           const mc::mutable_dict&     track_request) {
    dlog("receiving data: ${data}", ("data", data));

    std::string data_str(data.begin(), data.end());
    if (!data_str.empty() && data_str[0] == '\xFF') {
        return {false,
                mc::mutable_dict{
                    {"error", "Request(" + std::to_string(track_request["opcode"].as<uint16_t>()) +
                                  ") invalid response from the chip: " + data_str}}};
    }
    auto expect_data_len = track_request["expect_data_len"].as<int32_t>();
    auto [ok, result]    = unpack_response_data(data_str, expect_data_len);
    if (!ok) {
        return {ok, result};
    }
    // 检查错误码
    if (result["error_code"].as<uint8_t>() != success_code) {
        elog("receive error from the chip, error: ${error}",
             ("error", error_code_map.at(result["error_code"].as<uint8_t>())));
        return {false, mc::mutable_dict{
                           {"error", "receive error from the chip, error: " +
                                         error_code_map.at(result["error_code"].as<uint8_t>())}}};
    }

    if (result["opcode"].as<uint16_t>() != track_request["opcode"].as<uint16_t>()) {
        elog("received incorrect result from the chip! expected opcode ${expected}, actual: "
             "${actual}",
             ("expected", std::to_string(track_request["opcode"].as<uint16_t>()))(
                 "actual", std::to_string(result["opcode"].as<uint16_t>())));
        return {false,
                mc::mutable_dict{
                    {"error", "received incorrect result from the chip! expected opcode " +
                                  std::to_string(track_request["opcode"].as<uint16_t>()) +
                                  ", actual: " + std::to_string(result["opcode"].as<uint16_t>())}}};
    }

    return {ok, result};
}

mc::mutable_dict std_smbus::_unpack_response(const std::string& rsp_bin) {
    // 传入的rsp_bin 必须保证长度，不能被\x00截断
    mc::mutable_dict response;

    uint8_t count     = static_cast<uint8_t>(rsp_bin[0]);
    response["count"] = count;

    // 解析error_code (第1-2字节，uint16_t)
    uint16_t error_code =
        static_cast<uint8_t>(rsp_bin[1]) | (static_cast<uint8_t>(rsp_bin[2]) << 8);
    response["error_code"] = error_code;

    // 解析opcode (第3-4字节，uint16_t)
    uint16_t opcode    = static_cast<uint8_t>(rsp_bin[3]) | (static_cast<uint8_t>(rsp_bin[4]) << 8);
    response["opcode"] = opcode;

    // 解析total_length (第5-8字节，uint32_t)
    uint32_t total_length =
        static_cast<uint8_t>(rsp_bin[5]) | (static_cast<uint8_t>(rsp_bin[6]) << 8) |
        (static_cast<uint8_t>(rsp_bin[7]) << 16) | (static_cast<uint8_t>(rsp_bin[8]) << 24);
    response["total_length"] = total_length;

    // 解析length (第9-12字节，uint32_t)
    uint32_t length = static_cast<uint8_t>(rsp_bin[9]) | (static_cast<uint8_t>(rsp_bin[10]) << 8) |
                      (static_cast<uint8_t>(rsp_bin[11]) << 16) |
                      (static_cast<uint8_t>(rsp_bin[12]) << 24);
    response["length"] = length;

    // 获取数据部分 (从第13字节开始，长度为length指定的字节数)
    // 注意：这里假设length不会导致越界访问
    response["data"] = rsp_bin.substr(13, length);

    return response;
}

std::pair<bool, mc::mutable_dict> std_smbus::unpack_response_data(const std::string& rsp_bin,
                                                                  const int32_t expect_data_len) {
    auto rsp = _unpack_response(rsp_bin);

    constexpr uint8_t read_command_code = 0x21;
    auto return_crc_mcu_address = (m_mcu_address | 0x01); // 计算返回crc8时第二个mcu地址需要或0x01

    // 解析crc8
    auto rsp_crc             = static_cast<uint8_t>(rsp_bin.back());
    auto rsp_bin_without_crc = rsp_bin.substr(0, rsp_bin.length() - 1);

    std::string crc_input;
    crc_input += static_cast<char>(m_mcu_address);
    crc_input += static_cast<char>(read_command_code);
    crc_input += static_cast<char>(return_crc_mcu_address);
    crc_input += rsp_bin_without_crc;

    auto expect_crc = mc::crypto::CRC8().calculate(crc_input);
    if (rsp_crc != expect_crc) {
        elog("crc check error on std smbus response, expect: ${expect}, actual: ${actual}",
             ("expect", std::to_string(expect_crc))("actual", std::to_string(rsp_crc)));
        return {false, mc::mutable_dict{{"error", "crc check error on std smbus response"}}};
    }

    return {true, rsp};
}

std::pair<bool, std::vector<uint8_t>> std_smbus::send_and_receive(const std::string& data,
                                                                  uint32_t           len) {
    std::string crc_input;
    crc_input += static_cast<char>(m_mcu_address);
    crc_input += static_cast<char>(write_command_code);
    crc_input += data;

    auto crc = mc::crypto::CRC8().calculate(crc_input);

    auto request_data = data + static_cast<char>(crc);

    dlog("sending std smbus write command, data: ${data}, crc: ${crc}",
         ("data", data)("crc", std::to_string(crc)));

    try {
        uint32_t             write_cmd = static_cast<uint32_t>(write_command_code);
        std::vector<uint8_t> request_vec(request_data.begin(), request_data.end());
        uint32_t             read_cmd = static_cast<uint32_t>(read_command_code);
        auto                 result = m_ComboWriteRead_func(write_cmd, request_vec, read_cmd, len);
        return {true, result};
    } catch (const std::exception& e) {
        return {false, std::vector<uint8_t>()};
    }
}

std::pair<bool, std::vector<uint8_t>> std_smbus::send(uint32_t offset, const std::string& data) {
    ilog("send std smbus write command, offset: ${offset}, data: ${data}",
         ("offset", offset)("data", data));

    try {
        // 将字符串转换为vector<uint8_t>
        std::vector<uint8_t> data_vec(data.begin(), data.end());
        auto                 result = m_Write_func(offset, data_vec);
        return {true, result};
    } catch (const std::exception& e) {
        return {false, std::vector<uint8_t>()};
    }
}

std::pair<bool, std::vector<uint8_t>> std_smbus::read(uint32_t offset, uint32_t length) {
    ilog("read std smbus read command, offset: ${offset}, length: ${length}",
         ("offset", offset)("length", length));

    try {
        auto result = m_Read_func(offset, length);
        return {true, result};
    } catch (const std::exception& e) {
        return {false, std::vector<uint8_t>()};
    }
}

void std_smbus::register_ComboWriteRead_method(
    std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&, uint32_t, uint32_t)>
        func) {
    m_ComboWriteRead_func = func;
}

void std_smbus::register_Write_method(
    std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&)> func) {
    m_Write_func = func;
}

void std_smbus::register_Read_method(std::function<std::vector<uint8_t>(uint32_t, uint32_t)> func) {
    m_Read_func = func;
}

void std_smbus::register_WriteRead_method(
    std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func) {
    m_WriteRead_func = func;
}
} // namespace dev